#include "WOH_ParticleSystem.h"

CParticleSystem::CParticleSystem(void)
{
}

CParticleSystem::~CParticleSystem(void)
{
}
void CParticleSystem::ChangeRenderState(IDirect3DDevice9 *Device)
{
	Device->SetRenderState(D3DRS_LIGHTING, false);

	Device->SetRenderState(D3DRS_POINTSPRITEENABLE, true);
	Device->SetRenderState(D3DRS_POINTSCALEENABLE, true);
	Device->SetRenderState(D3DRS_POINTSIZE, FtoD(m_Size));
	Device->SetRenderState(D3DRS_POINTSIZE_MIN, FtoD(0.0f));
	Device->SetRenderState(D3DRS_POINTSCALE_A, FtoD(0.0f));
	Device->SetRenderState(D3DRS_POINTSCALE_B, FtoD(0.0f));
	Device->SetRenderState(D3DRS_POINTSCALE_C, FtoD(1.0f));

	Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

	Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}

DWORD CParticleSystem::FtoD(float f)
{
	return *((DWORD*)&f);
}

void CParticleSystem::OldRenderState(IDirect3DDevice9 *Device)
{
	Device->SetRenderState(D3DRS_POINTSPRITEENABLE, false);
	Device->SetRenderState(D3DRS_POINTSCALEENABLE, false);
	Device->SetRenderState(D3DRS_LIGHTING, true);
	Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}

bool CParticleSystem::Init(IDirect3DDevice9 *Device, LPCWSTR filename)
{
	if(FAILED(Device->CreateVertexBuffer(m_vbSize * sizeof(ParticleVertex), D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY, PARTICLE_FVF,
							   D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL)))
	{
		LOG::Instance()->OutputError("Create Vertex Buffer for particle system  Failed.");
		return false;
	}
	if(FAILED(D3DXCreateTextureFromFile(Device, filename, &m_pTex)))
	{
		LOG::Instance()->OutputError("Create particle Texture from file  Failed.");
		return false;
	}
	LOG::Instance()->OutputSuccess("ParticleSystem Initiation  Successful");
	return true;
}

void CParticleSystem::AddParticle()
{
	Attribute attribute;
	ResetParticle(&attribute);
	particles.push_back(attribute);
}

void CParticleSystem::Reset()
{
	list<Attribute>::iterator i;
	for(i = particles.begin(); i!=particles.end(); i++)
	{
		ResetParticle(&(*i));
	}
}

bool CParticleSystem::IsEmpty()
{
	return particles.empty();
}

bool CParticleSystem::IsDead()
{
	list<Attribute>::iterator i;
	for(i = particles.begin(); i!=particles.end(); i++)
	{
		if(i->live)
		{
			return false;
		}
	}
	return true;
}

void CParticleSystem::RemoveDeadParticle()
{
	list<Attribute>::iterator i;
	for(i = particles.begin(); i!=particles.end(); i++)
	{
		if(!(i->live))
		{
			particles.erase(i);
			if(this->particles.empty())
                 break;
		    i=particles.begin();
		}

	}
}

void CParticleSystem::Render(IDirect3DDevice9 *Device)
{
	if(!IsEmpty())
	{
		D3DXMATRIX temp;
		D3DXMatrixIdentity(&temp);
		Device->SetTransform(D3DTS_WORLD, &temp);
		ChangeRenderState(Device);
		Device->SetTexture(0, m_pTex);
		Device->SetFVF(PARTICLE_FVF);
		Device->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(ParticleVertex));

		if(m_vboffset >= m_vbSize)
		{
			m_vboffset = 0;
		}

		ParticleVertex *v = NULL;
		m_pVertexBuffer->Lock(m_vboffset * sizeof(ParticleVertex), m_vbBatchSize * sizeof(ParticleVertex), (void**)&v, m_vboffset?D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD);

		DWORD numParticlesInBatch = 0;

		list<Attribute>::iterator i;
		for(i = particles.begin(); i!=particles.end(); i++)
		{
			if(i->live)
			{
				v->color = i->color;
				v->x = i->position.x;
				v->y = i->position.y;
				v->z = i->position.z;
				v++;
				numParticlesInBatch++;
				if(numParticlesInBatch == m_vbBatchSize)
				{
					m_pVertexBuffer->Unlock();
					Device->SetRenderState(D3DRS_POINTSIZE, i->psize);
					Device->DrawPrimitive(D3DPT_POINTLIST, m_vboffset, m_vbBatchSize);

					m_vboffset += m_vbBatchSize;
					if(m_vboffset >= m_vbSize)
						m_vboffset = 0;
					m_pVertexBuffer->Lock(m_vboffset * sizeof(ParticleVertex), m_vbBatchSize * sizeof(ParticleVertex), (void**)&v, m_vboffset?D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD);
					numParticlesInBatch = 0;
				}
			}
		}
		m_pVertexBuffer->Unlock();
		if(numParticlesInBatch)
		{
			Device->DrawPrimitive(D3DPT_POINTLIST, m_vboffset, numParticlesInBatch);
		}

		m_vboffset += m_vbBatchSize;
		OldRenderState(Device);
	}
}

float CParticleSystem::GetRandomFloat(float lowBound, float highBound)
{
	if( lowBound >= highBound )
		return lowBound;
	float f = (rand() % 10000) * 0.0001f; 

	return (f * (highBound - lowBound)) + lowBound; 
}

void CParticleSystem::GetRandomVector(D3DXVECTOR3* out,D3DXVECTOR3* min,D3DXVECTOR3* max)
{
	out->x = GetRandomFloat(min->x, max->x);
	out->y = GetRandomFloat(min->y, max->y);
	out->z = GetRandomFloat(min->z, max->z);
}
